package com.malty.gateway.sign;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.malty.common.ErrorCode;
import com.malty.common.exception.BusinessException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.DigestUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * post application/json方式 请求验签
 *
 * @author malty
 */
public class PostJsonApiSignStrategy extends ApiSignStrategy {

    @Override
    public Mono<Map<String, String>> getParamMap(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        // 先判断Content-Type是不是application/json
        MediaType contentType = headers.getContentType();
        if (contentType == null || !MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
            return Mono.error(new BusinessException(ErrorCode.PARAMS_ERROR, "Content-Type must be application/json"));
        }

        // 获取缓存的 DataBuffer
        DataBuffer cachedBodyBuffer = exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
        // 如果没有缓存的请求体或者请求体为空
        if (cachedBodyBuffer == null || cachedBodyBuffer.readableByteCount() == 0) {
            Map<String, String> paramMap = new HashMap<>(extractHeaderParam(request));
            paramMap.remove("sign");
            return Mono.just(Collections.unmodifiableMap(paramMap));
        }

        String bodyStr = cachedBodyBuffer.toString(StandardCharsets.UTF_8);

        try {
            JSONObject jsonMap = JSON.parseObject(bodyStr);
            Map<String, String> paramMap = new HashMap<>();
            if (jsonMap != null && !jsonMap.isEmpty()) {
                paramMap = jsonMap.entrySet().stream()
                        .filter(entry -> entry.getKey() != null && entry.getValue() != null)
                        .collect(Collectors.toMap(
                                Map.Entry::getKey,
                                entry -> entry.getValue().toString()
                        ));
            }

            paramMap.putAll(extractHeaderParam(request));

            paramMap.remove("sign");

            return Mono.just(Collections.unmodifiableMap(paramMap));
        } catch (Exception e) {
            return Mono.error(new BusinessException(ErrorCode.PARAMS_ERROR, "签名校验失败: 无效的 JSON 请求体"));
        }
    }

    @Override
    public String doCalculateSign(String signKey, String sourceStr) {
        String plaintext = String.format("%s%s", signKey, sourceStr);
        return DigestUtils.md5DigestAsHex(plaintext.getBytes(StandardCharsets.UTF_8));
    }
}
